Skip to content

feat(checkout): CHECKOUT-10026 Connect new Google Places API behind feature flag#3142

Open
bc-maxy wants to merge 6 commits into
masterfrom
checkout-10026-wiring
Open

feat(checkout): CHECKOUT-10026 Connect new Google Places API behind feature flag#3142
bc-maxy wants to merge 6 commits into
masterfrom
checkout-10026-wiring

Conversation

@bc-maxy

@bc-maxy bc-maxy commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What/Why?

This is part II of this change: #3135, where I actually wire up this new Places API service to production, but hide new functionality behind a feature flag.

When the CHECKOUT-10026.new_google_places_api experiment is on, address autocomplete uses Google's new Places API (session-token based, AutocompleteSuggestion/Place classes) instead of the legacy API service. If the new API is denied due to permissions error (gRPC PERMISSION_DENIED), we assume that the service is not enabled for the store and silently fall back to the legacy API for the rest of the session. No visible errors for users and no visible change between old and new service - this is an infrastructure swap behind a feature flag.

NOTE: when the flag is off, there will be no change for end users, However, the code won't be byte-to-byte identical: we initialize newGooglePlacesApiServiceRef.current with new NewGooglePlacesApiService(apiKey) unconditionally on mount, even when the flag is off. It has no side effects though (no network call, no script load - the script loader inside of it is lazy), so this is just a trivial extra allocation without any functional change for end users.

CHECKOUT-10026.new_google_places_api is currently set to true in integration and staging and false for everyone in production.

Rollout/Rollback

Set CHECKOUT-10026.new_google_places_api experiment to false if the store where we rolled out the feature is affected. For issues with other stores revert the PR.

Testing

New CI tests + manual testing.

If you want to test yourself you can use these API keys in https://console.cloud.google.com/apis/credentials?project=bc-hackathon-env:

  1. Google Maps API Key test - our old API key that still has access to old Places API and has no access to new Places API. Represents old users.
  2. Maxs Places API Key Test - our new API key that has access to new Places API and no access to old Places API. Represents new users.

My tests are below:

With experiment set to false (same as current production)

Existing customers with old API keys:

Screen.Recording.2026-06-30.at.3.16.03.PM.mov

New customers with new API keys (doesn't work):

Screen.Recording.2026-06-30.at.3.18.15.PM.mov

With experiment set to true

Existing customers with old API keys (Initial 403, then fallback to the old service. Billing step will remember that we fallback and skips new api):

Screen.Recording.2026-06-30.at.3.30.18.PM.mov

New customers with new API keys (just works):

Screen.Recording.2026-06-30.at.3.24.12.PM.mov

Note

Medium Risk
Touches a core checkout address flow and external Google APIs with session-wide fallback behavior; rollout is gated by a feature flag and preserves legacy behavior when off or denied.

Overview
Checkout address line 1 autocomplete can use Google's new Places API when the CHECKOUT-10026.new_google_places_api experiment is on; AddressForm reads that flag and passes isNewPlacesApiEnabled through GoogleAutocompleteFormField.

Autocomplete logic moves into useGoogleAutocomplete, which prefers the new service for suggestions and place details and otherwise uses the legacy Maps Places client. On gRPC permission denied, it falls back to legacy for that request and latches so later keystrokes skip the new API for the rest of the checkout session; other errors clear suggestions or skip selection without switching APIs. With the flag off, only legacy runs. When the new path is active, legacy shares the same Maps script loader to avoid loading the JS API twice.

GoogleAutocomplete is thinned to the shared UI hook; legacy prediction mapping lives in utils.ts. New component tests cover the happy path, permission fallback/latching, transient failures, and flag-off behavior.

Reviewed by Cursor Bugbot for commit d23c96d. Bugbot is set up for automated code reviews on this repo. Configure here.

@bc-maxy

bc-maxy commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

cursor review

Comment thread packages/core/src/app/address/googleAutocomplete/GoogleAutocomplete.tsx Outdated
Comment thread packages/core/src/app/address/googleAutocomplete/GoogleAutocomplete.tsx Outdated
@bc-maxy

bc-maxy commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

cursor review

Comment thread packages/core/src/app/address/googleAutocomplete/GoogleAutocomplete.tsx Outdated
@bc-maxy

bc-maxy commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c05b09f. Configure here.

const onSelectHandler = (item: AutocompleteItem) => {
const isUsingLegacyApi = () => !isNewPlacesApiEnabled || newGooglePlacesApiState.isUnavailable;

const finalizeSelection = (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like many lines are added to this file.
Is it possible to move them out as separate file(s) to keep this file small?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of the PR looks good.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for checking it @bc-peng 🙏 I made changes here: 33ca5c2. Is this what you had in mind?

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 33ca5c2. Configure here.

Comment thread packages/core/src/app/address/googleAutocomplete/useGoogleAutocomplete.ts Outdated
Comment on lines +81 to +97
const fetchSuggestionsWithLegacyApi = (input: string): void => {
const service = googleAutocompleteServiceRef.current;

if (!service) return;

service.getAutocompleteService().then((autocompleteService) => {
autocompleteService.getPlacePredictions(
{ input, types: types || ['geocode'], componentRestrictions },
(results) => {
setItems(toAutocompleteItems(results ?? undefined));
},
);
});
};

const fetchSuggestionsWithNewApi = (input: string): void => {
const service = newGooglePlacesApiServiceRef.current;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not blocking, just a thought. Does it makes sense to have 2 classes or similar for old and new aPI and then based on the isUsingLegacyApi we use the correct class in the hook to avoid naming such as fetchSuggestionsWithNewApi/fetchSuggestionsWithLegacyApi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants